package math.geom2d;

import math.base.CCS2d;
import math.base.Point2d;
import math.base.Vector2d;
import math.geomUtils.CurveType;

/**
 * @author Saga
 *
 */
public class Circle2d extends ConicalCurve2d {

    public Circle2d(final CCS2d _pos, final double _radius) {
        this.pos = _pos;
        if (_radius < 0.0) {
            throw new ArithmeticException("");
        }
        this.radius = _radius;
    }

    @Override
    public final CurveType getCurveType() {
        return CurveType.CIRCLE;
    }

    @Override
    public final void reverse() {
        this.pos.yReverse();
    }

    @Override
    public final Curve2d reversed() {
        return new Circle2d(this.pos.yReversed(), radius);
    }

    @Override
    public final boolean isPeriodic() {
        return true;
    }

    @Override
    public final double getPeriod() {
        return Math.PI * 2.0;
    }

    @Override
    public final double getFirstParameter() {
        return 0.0;
    }

    @Override
    public final double getLastParameter() {
        return Math.PI * 2.0;
    }

    public final void setRadius(final double _radius) {
        if (_radius < 0.0) {
            throw new ArithmeticException("");
        }
        this.radius = _radius;
    }

    public final double getRadius() {
        return this.radius;
    }

    @Override
    public final Point2d calcValue(final double _param) {
        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        return this.pos.getOrigin().added(this.pos.getDirectX().multiplied(cosVal).added(this.pos
                .getDirectY().multiplied(sinVal)));
    }

    @Override
    public final Vector2d calcDerivative(final double _param) {
        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        return this.pos.getDirectX().multiplied(-sinVal).added(
                this.pos.getDirectY().multiplied(cosVal));
    }

    @Override
    public final Vector2d calcDerivativeN(final double _param, final int _order) {
        if (_order < 1) {
            throw new ArithmeticException("");
        }

        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        switch (_order % 4) {
        case 0:
            return this.pos.getDirectX().multiplied(cosVal).added(
                    this.pos.getDirectY().multiplied(sinVal));

        case 1:
            return this.pos.getDirectX().multiplied(-sinVal).added(
                    this.pos.getDirectY().multiplied(cosVal));

        case 2:
            return this.pos.getDirectX().multiplied(-cosVal).added(
                    this.pos.getDirectY().multiplied(-sinVal));

        case 3:
            return this.pos.getDirectX().multiplied(sinVal).added(
                    this.pos.getDirectY().multiplied(-cosVal));
        }

        throw new ArithmeticException("");
    }

    @Override
    public final void calcValueAndDerivative1(final double _param, final Point2d _pt,
            final Vector2d _der1) {
        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        _pt.setCoord(this.pos.getOrigin().getCoord()
                .added(this.pos.getDirectX().getCoord().multiplied(cosVal)
                        .added(this.pos.getDirectY().getCoord().multiplied(sinVal))));
        _der1.setCoord(this.pos.getDirectX().getCoord().multiplied(-sinVal)
                .added(this.pos.getDirectY().getCoord().multiplied(cosVal)));
    }

    @Override
    public final void calcValueAndDerivative2(final double _param, final Point2d _pt,
            final Vector2d _der1, final Vector2d _der2) {
        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        _pt.setCoord(this.pos.getOrigin().getCoord()
                .added(this.pos.getDirectX().getCoord().multiplied(cosVal)
                        .added(this.pos.getDirectY().getCoord().multiplied(sinVal))));
        _der1.setCoord(this.pos.getDirectX().getCoord().multiplied(-sinVal)
                .added(this.pos.getDirectY().getCoord().multiplied(cosVal)));
        _der2.setCoord(this.pos.getDirectX().getCoord().multiplied(-cosVal)
                .added(this.pos.getDirectY().getCoord().multiplied(-sinVal)));
    }

    @Override
    public final void calcValueAndDerivative3(final double _param, final Point2d _pt,
            final Vector2d _der1, final Vector2d _der2, final Vector2d _der3) {
        final double cosVal = Math.cos(_param) * radius;
        final double sinVal = Math.sin(_param) * radius;

        _pt.setCoord(this.pos.getOrigin().getCoord()
                .added(this.pos.getDirectX().getCoord().multiplied(cosVal)
                        .added(this.pos.getDirectY().getCoord().multiplied(sinVal))));
        _der1.setCoord(this.pos.getDirectX().getCoord().multiplied(-sinVal)
                .added(this.pos.getDirectY().getCoord().multiplied(cosVal)));
        _der2.setCoord(this.pos.getDirectX().getCoord().multiplied(-cosVal)
                .added(this.pos.getDirectY().getCoord().multiplied(-sinVal)));
        _der3.setCoord(this.pos.getDirectX().getCoord().multiplied(sinVal)
                .added(this.pos.getDirectY().getCoord().multiplied(-cosVal)));
    }

    @Override
    public final Circle2d clone() {
        return new Circle2d(this.pos.clone(), this.radius);
    }

    private double radius;
}
